package com.panfeng.xcloud.scheduler.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.panfeng.xcloud.boss.provider.dto.request.OperateBotReqDTO;
import com.panfeng.xcloud.boss.provider.dto.request.QuantBotDetailDTO;
import com.panfeng.xcloud.common.core.candle.CandleEntry;
import com.panfeng.xcloud.common.core.candle.Entry;
import com.panfeng.xcloud.common.core.candle.MyCandleEntry;
import com.panfeng.xcloud.common.core.candle.SuitEntry;
import com.panfeng.xcloud.common.core.configuration.DataDictionaryConfig;
import com.panfeng.xcloud.common.core.enums.*;
import com.panfeng.xcloud.common.core.exceptions.BaseBizException;
import com.panfeng.xcloud.common.core.utils.FinanceIndicatorsUtil;
import com.panfeng.xcloud.scheduler.service.ICalculatePriceService;
import com.panfeng.xcloud.scheduler.service.IFeignService;
import groovy.util.logging.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 相关数据计算类
 */
@Slf4j
@Service
public class CalculatePriceServiceImpl implements ICalculatePriceService {

    private static Logger log = LoggerFactory.getLogger(CalculatePriceServiceImpl.class);

    @Resource
    private DataDictionaryConfig dataDictionaryConfig;

    @Autowired
    private IFeignService iFeignService;

    @Override
    public BigDecimal[] calculatePrice(JSONObject depth, QuantBotDetailDTO quantBotDetailDTO) throws Exception {
        Integer exchangeType = Integer.valueOf(quantBotDetailDTO.getExchangeType());
        if (exchangeType == ExchangeTypeEnum.OKEX.getCode()) {
            return calculateOkexPrice(depth, quantBotDetailDTO);
        } else if (exchangeType == ExchangeTypeEnum.HUOBI.getCode()) {
            return calculateHuobiPrice(depth, quantBotDetailDTO);
        } else if (exchangeType == ExchangeTypeEnum.BINANCE.getCode()) {
            return calculateBinancePrice(depth, quantBotDetailDTO);
        } else {
            throw new BaseBizException(ResponseStatusEnum.EXCHANGE_TYPE_NOT_EXIST);
        }
    }

    @Override
    public List[] calculateKdj(JSONObject candleStick) throws Exception {
        JSONArray candlesticks = candleStick.getJSONArray("candlesticks");
        List<MyCandleEntry> candleEntries = new ArrayList<>();
        for (int i = 0; i < candlesticks.size(); i++) {
            JSONObject obj = (JSONObject) (candlesticks.get(i));
            MyCandleEntry candleEntry = new MyCandleEntry(i, obj.getFloatValue("high"), obj.getFloatValue("low"), obj.getFloatValue("open"), obj.getFloatValue("close"), obj.getFloatValue("volume"), obj.getLongValue("openTime"), obj.getLongValue("closeTime"), obj.getBigDecimal("quoteAssetVolume"));
            candleEntries.add(candleEntry);
        }
        List[] kdj = FinanceIndicatorsUtil.getKDJ(candleEntries, 24, 3, 3, 1);
        return kdj;
    }

    @Override
    public float[] calculateMA(JSONObject candleStick, MovingAveragePeriodEnum maPeriod) throws Exception {
        JSONArray candlesticks = candleStick.getJSONArray("candlesticks");
        List<SuitEntry> candleEntries = new ArrayList<>();
        for (int i = 0; i < candlesticks.size(); i++) {
            JSONObject obj = (JSONObject) (candlesticks.get(i));
            MyCandleEntry candleEntry = new MyCandleEntry(i, obj.getFloatValue("high"), obj.getFloatValue("low"), obj.getFloatValue("open"), obj.getFloatValue("close"), obj.getFloatValue("volume"), obj.getLongValue("openTime"), obj.getLongValue("closeTime"), obj.getBigDecimal("quoteAssetVolume"));
            candleEntries.add(candleEntry);
        }
        List<Entry> maList = FinanceIndicatorsUtil.getMA(candleEntries, maPeriod.code);

        float m = (maList.get(maList.size() - 1)).getY();
        float m_last = (maList.get(maList.size() - 2)).getY();
        float m_last2 = (maList.get(maList.size() - 3)).getY();
        float m_last3 = (maList.get(maList.size() - 4)).getY();

        float[] mas = new float[]{m, m_last, m_last2, m_last3};
        return mas;
    }

    @Override
    public List<Entry> calculateEMA(JSONObject candleStick, MovingAveragePeriodEnum maPeriod) throws Exception {
        JSONArray candlesticks = candleStick.getJSONArray("candlesticks");
        List<SuitEntry> candleEntries = new ArrayList<>();
        for (int i = 0; i < candlesticks.size(); i++) {
            JSONObject obj = (JSONObject) (candlesticks.get(i));
            MyCandleEntry candleEntry = new MyCandleEntry(i, obj.getFloatValue("high"), obj.getFloatValue("low"), obj.getFloatValue("open"), obj.getFloatValue("close"), obj.getFloatValue("volume"), obj.getLongValue("openTime"), obj.getLongValue("closeTime"), obj.getBigDecimal("quoteAssetVolume"));
            candleEntries.add(candleEntry);
        }
        List<Entry> maList = FinanceIndicatorsUtil.getEMA(candleEntries, maPeriod.code);
        return maList;
    }

    @Override
    public BigDecimal getNeedAskPieces(QuantBotDetailDTO quantBotDetailDTO) {
        Integer currentAddCount = Integer.valueOf(quantBotDetailDTO.getCurrentAddCount());
        Integer maxAddCount = Integer.valueOf(quantBotDetailDTO.getAddCount());
        BigDecimal times = BigDecimal.ZERO;
        if (quantBotDetailDTO.getAddPositionTimesModelSwitch().equals(AddPositionTimesModelSwitchTypeEnum.FIXED.getCode())) {
            times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.position_times.getCode()));
            BigDecimal pow = BigDecimal.ZERO;
            if (currentAddCount < maxAddCount) {
                pow = times.pow(currentAddCount + 1);
            } else {
                pow = times.pow(maxAddCount);
            }
            return new BigDecimal(quantBotDetailDTO.getInitOrderPosition()).multiply(pow);
        } else if (quantBotDetailDTO.getAddPositionTimesModelSwitch().equals(AddPositionTimesModelSwitchTypeEnum.DYNAMIC.getCode())) {
            switch (currentAddCount) {
                case 0:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times1.getCode()));
                    break;
                case 1:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times2.getCode()));
                    break;
                case 2:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times3.getCode()));
                    break;
                case 3:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times4.getCode()));
                    break;
                case 4:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times5.getCode()));
                    break;
                case 5:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times6.getCode()));
                    break;
                case 6:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times7.getCode()));
                    break;
                case 7:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times8.getCode()));
                    break;
                case 8:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times9.getCode()));
                    break;
                case 9:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times10.getCode()));
                    break;
                case 10:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times11.getCode()));
                    break;
                case 11:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times12.getCode()));
                    break;
                case 12:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times13.getCode()));
                    break;
                case 13:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times14.getCode()));
                    break;
                case 14:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times15.getCode()));
                    break;
                case 15:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times16.getCode()));
                    break;
                case 16:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times17.getCode()));
                    break;
                case 17:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times18.getCode()));
                    break;
                case 18:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times19.getCode()));
                    break;
                case 19:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times20.getCode()));
                    break;
                default:
                    times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.dynamic_position_times20.getCode()));
            }
            return new BigDecimal(quantBotDetailDTO.getInitOrderPosition()).multiply(times);
        } else {
            //不存在该种倍投模式
            throw new BaseBizException(ResponseStatusEnum.TIMES_MODEL_NOT_EXIST_ERROR);
        }
    }

    @Override
    public BigDecimal getAntiNeedAskPieces(QuantBotDetailDTO quantBotDetailDTO) {
        Integer currentAntiAddCount = Integer.valueOf(quantBotDetailDTO.getCurrentAntiAddCount());
        Integer maxAntiAddCount = Integer.valueOf(quantBotDetailDTO.getAntiAddCount());
        BigDecimal times = BigDecimal.ZERO;
        times = new BigDecimal(dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.anti_position_times.getCode()));
        BigDecimal pow = BigDecimal.ZERO;
        if (currentAntiAddCount < maxAntiAddCount) {
            pow = times.pow(currentAntiAddCount + 1);
        } else {
            pow = times.pow(maxAntiAddCount);
        }
        return new BigDecimal(quantBotDetailDTO.getInitOrderPosition()).multiply(pow);
    }

    private BigDecimal[] calculateOkexPrice(JSONObject depth, QuantBotDetailDTO quantBotDetailDTO) {
        BigDecimal[] array = {BigDecimal.ZERO, BigDecimal.ZERO};
        BigDecimal contractCodeValue = new BigDecimal(quantBotDetailDTO.getContractCodeValue());

        //计算买盘合适的价格
        BigDecimal bidPositionAmount = BigDecimal.ZERO;
        BigDecimal restBidPieces = new BigDecimal(quantBotDetailDTO.getPositionPiece());
        JSONArray bids = depth.getJSONArray("bids");
        for (int i = 0; i < bids.size(); i++) {
            JSONArray bid = (JSONArray) (bids.get(i));
            BigDecimal price = new BigDecimal(String.valueOf(bid.get(0)));
            BigDecimal size = new BigDecimal(String.valueOf(bid.get(1)));
            if (size.compareTo(restBidPieces) >= 0) {
                bidPositionAmount = bidPositionAmount.add(restBidPieces.multiply(contractCodeValue).multiply(price));
                break;
            } else {
                bidPositionAmount = bidPositionAmount.add(size.multiply(contractCodeValue).multiply(price));
                restBidPieces = restBidPieces.subtract(size);
            }
        }
        BigDecimal bidPrice = bidPositionAmount.divide(new BigDecimal(quantBotDetailDTO.getPositionNum()), 8, BigDecimal.ROUND_DOWN);

        //计算卖盘合适的价格
        BigDecimal askPositionAmount = BigDecimal.ZERO;
        BigDecimal needAskPieces = getNeedAskPieces(quantBotDetailDTO);
        BigDecimal restAskPieces = needAskPieces;
        JSONArray asks = depth.getJSONArray("asks");
        for (int i = 0; i < asks.size(); i++) {
            JSONArray ask = (JSONArray) (asks.get(i));
            BigDecimal price = new BigDecimal(String.valueOf(ask.get(0)));
            BigDecimal size = new BigDecimal(String.valueOf(ask.get(1)));
            if (size.compareTo(restAskPieces) >= 0) {
                askPositionAmount = askPositionAmount.add(restAskPieces.multiply(contractCodeValue).multiply(price));
                break;
            } else {
                askPositionAmount = askPositionAmount.add(size.multiply(contractCodeValue).multiply(price));
                restAskPieces = restAskPieces.subtract(size);
            }
        }
        BigDecimal askPrice = askPositionAmount.divide(needAskPieces.multiply(contractCodeValue), 8, BigDecimal.ROUND_DOWN);
        array[0] = bidPrice;
        array[1] = askPrice;
        return array;
    }

    private BigDecimal[] calculateHuobiPrice(JSONObject depth, QuantBotDetailDTO quantBotDetailDTO) {
        BigDecimal[] array = {BigDecimal.ZERO, BigDecimal.ZERO};
        BigDecimal contractCodeValue = new BigDecimal(quantBotDetailDTO.getContractCodeValue());

        //计算买盘合适的价格
        BigDecimal bidPositionAmount = BigDecimal.ZERO;
        BigDecimal restBidPieces = new BigDecimal(quantBotDetailDTO.getPositionPiece());
        JSONArray bids = depth.getJSONArray("bids");
        for (int i = 0; i < bids.size(); i++) {
            JSONArray bid = (JSONArray) (bids.get(i));
            BigDecimal price = new BigDecimal(String.valueOf(bid.get(0)));
            BigDecimal size = new BigDecimal(String.valueOf(bid.get(1)));
            if (size.compareTo(restBidPieces) >= 0) {
                bidPositionAmount = bidPositionAmount.add(restBidPieces.multiply(contractCodeValue).multiply(price));
                break;
            } else {
                bidPositionAmount = bidPositionAmount.add(size.multiply(contractCodeValue).multiply(price));
                restBidPieces = restBidPieces.subtract(size);
            }
        }
        BigDecimal bidPrice = bidPositionAmount.divide(new BigDecimal(quantBotDetailDTO.getPositionNum()), 8, BigDecimal.ROUND_DOWN);

        //计算卖盘合适的价格
        BigDecimal askPositionAmount = BigDecimal.ZERO;
        BigDecimal needAskPieces = getNeedAskPieces(quantBotDetailDTO);
        BigDecimal restAskPieces = needAskPieces;
        JSONArray asks = depth.getJSONArray("asks");
        for (int i = 0; i < asks.size(); i++) {
            JSONArray ask = (JSONArray) (asks.get(i));
            BigDecimal price = new BigDecimal(String.valueOf(ask.get(0)));
            BigDecimal size = new BigDecimal(String.valueOf(ask.get(1)));
            if (size.compareTo(restAskPieces) >= 0) {
                askPositionAmount = askPositionAmount.add(restAskPieces.multiply(contractCodeValue).multiply(price));
                break;
            } else {
                askPositionAmount = askPositionAmount.add(size.multiply(contractCodeValue).multiply(price));
                restAskPieces = restAskPieces.subtract(size);
            }
        }
        BigDecimal askPrice = askPositionAmount.divide(needAskPieces.multiply(contractCodeValue), 8, BigDecimal.ROUND_DOWN);
        array[0] = bidPrice;
        array[1] = askPrice;
        return array;
    }

    private BigDecimal[] calculateBinancePrice(JSONObject depth, QuantBotDetailDTO quantBotDetailDTO) throws Exception {
        BigDecimal[] array = {BigDecimal.ZERO, BigDecimal.ZERO};

        //计算买盘合适的价格
        BigDecimal bidPositionAmount = BigDecimal.ZERO;
        BigDecimal restBidPieces = new BigDecimal(quantBotDetailDTO.getPositionPiece());
        JSONArray bids = depth.getJSONArray("bids");
        for (int i = 0; i < bids.size(); i++) {
            JSONArray bid = (JSONArray) (bids.get(i));
            BigDecimal price = new BigDecimal(String.valueOf(bid.get(0)));
            BigDecimal size = new BigDecimal(String.valueOf(bid.get(1)));
            if (size.compareTo(restBidPieces) >= 0) {
                bidPositionAmount = bidPositionAmount.add(restBidPieces.multiply(price));
                break;
            } else {
                bidPositionAmount = bidPositionAmount.add(size.multiply(price));
                restBidPieces = restBidPieces.subtract(size);
            }
        }
        BigDecimal bidPrice = bidPositionAmount.divide(new BigDecimal(quantBotDetailDTO.getPositionNum()), 8, BigDecimal.ROUND_DOWN);

        //计算卖盘合适的价格
        BigDecimal askPositionAmount = BigDecimal.ZERO;
        BigDecimal needAskPieces = getNeedAskPieces(quantBotDetailDTO);

        OperateBotReqDTO operateBotReqDTO = new OperateBotReqDTO();
        operateBotReqDTO.setExchangeType(Integer.valueOf(quantBotDetailDTO.getExchangeType()));
        operateBotReqDTO.setUserId(quantBotDetailDTO.getUserId());
        operateBotReqDTO.setDestinationCoin(quantBotDetailDTO.getDestinationCoin());
        operateBotReqDTO.setSourceCoin(quantBotDetailDTO.getSourceCoin());
        operateBotReqDTO.setDirection(quantBotDetailDTO.getDirection());

        needAskPieces = iFeignService.toSetOpenOrderAmount(operateBotReqDTO, needAskPieces);

        BigDecimal restAskPieces = needAskPieces;
        JSONArray asks = depth.getJSONArray("asks");
        for (int i = 0; i < asks.size(); i++) {
            JSONArray ask = (JSONArray) (asks.get(i));
            BigDecimal price = new BigDecimal(String.valueOf(ask.get(0)));
            BigDecimal size = new BigDecimal(String.valueOf(ask.get(1)));
            if (size.compareTo(restAskPieces) >= 0) {
                askPositionAmount = askPositionAmount.add(restAskPieces.multiply(price));
                break;
            } else {
                askPositionAmount = askPositionAmount.add(size.multiply(price));
                restAskPieces = restAskPieces.subtract(size);
            }
        }
        BigDecimal askPrice = askPositionAmount.divide(needAskPieces, 8, BigDecimal.ROUND_DOWN);
        array[0] = bidPrice;
        array[1] = askPrice;
        return array;
    }

}
